﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using FFmpegVideoClip.Configuration;
using FFmpegVideoClip.Lib;
using FFmpegVideoClip.Lib.AdvancedFragmentProcessor;
using FFmpegVideoClip.Lib.AdvancedFragmentProcessor.InternalProcessor;
using PropertyChanged;
using UtilityToolsCollect.ObjectsLibrary.ProcessManage;
using UtilityToolsCollect.WinSystemUtilityTools;

namespace FFmpegVideoClip.Mode.Files
{
    [AddINotifyPropertyChangedInterface]
    public class FilesMode
    {
        public static FilesMode Instance { get; private set; } = new();
        public ObservableCollection<FileInfo> FileInfos { get; set; } = new();
        public Preset CurrentPrest { get; set; } = new();
        public string OutPath { get; set; } = string.Empty;
        public bool IsEnableSourceDirectory { get; set; } = false;
        public static void Execute()
        {
            if(string.IsNullOrWhiteSpace(Instance.OutPath))
            {
                MessageBoxEx.Show(Application.Current.MainWindow, "未指定输出路径", "输出路径错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            
            if(!Directory.Exists(Instance.OutPath))
            {
                if (MessageBoxEx.Show(Application.Current.MainWindow, "路径不存在，是否创建路径", "输出路径异常", MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.Cancel)
                    return;
                try
                {
                    Directory.CreateDirectory(Instance.OutPath);
                }
                catch (Exception exc)
                {
                    MessageBoxEx.Show(Application.Current.MainWindow, $"指定的路径信息无法创建路径：\r\n{exc.Message}", "路径错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
            }
            LogManage.Clear();  
            
            bool IsErr = false;
            bool IsSuccess = false;
            Instance.CurrentPrest.Flows.ForEach(p => p.UpdateIndexValues());
            ScriptManage.CompilationScript();
            ProcessStream[] pss = Instance.FileInfos.Select(p => new ProcessStream(p, Instance.CurrentPrest.Flows)).ToArray();
            MainWindow window = (MainWindow)Application.Current.MainWindow;
            window.StatusProgressShow();
            window.StatusProgress.Maximum = pss.Sum(p=>p.Modules.Sum(p2=>p2.successAndProgress.TotalSeconds));
            Tools.CurrentCancelToken = new CancellationTokenSource();

            
            System.Windows.Threading.DispatcherTimer dispatcherTimer = new() { Interval = TimeSpan.FromMilliseconds(1000) };
            dispatcherTimer.Tick += Progress;
            SetThreadExecutionStateTools.PreventSleep();
            dispatcherTimer.Start();
            void Progress(object? sender, EventArgs e)
            {
                if (!IsSuccess && !Tools.IsCancellationRequested)
                {
                    window.StatusProgress.Value = pss.Sum(p => p.Modules.Sum(p => p.successAndProgress.CompletedTimingSeconds));
                    return;
                }
                SetThreadExecutionStateTools.Restore();
                dispatcherTimer.Stop();
                dispatcherTimer.Tick -= Progress;
                dispatcherTimer = null!;
            }

            Instance.FileInfos.ForEach(fi => fi.CompletionStatus = CompletionStatusEnum.NotStart);
            Task.Factory.StartNew(() => {
                try
                {
                    Parallel.ForEach(pss, new ParallelOptions { MaxDegreeOfParallelism = Config.MaxThread, TaskScheduler = TaskSchedulerSelector.Get, CancellationToken = Tools.CurrentCancelToken.Token }, ps =>
                    {
                        try
                        {
                            if (Tools.IsCancellationRequested)
                                return;
                            IsErr = !ps.Execute();
                        }
                        catch (Exception exc)
                        {
                            IsErr = true;
                            if (Tools.IsCancellationRequested)
                                return;
                            LogManage.WriteExceptionInfo($"[{ps.ExecuteFileInfo.FullPath}]：", exc, true);
                        }
                    });
                }
                catch (OperationCanceledException) { }
                catch (Exception exc)
                {
                    LogManage.WriteExceptionInfo($"[{nameof(FilesMode)}] 异常：", exc, true);
                }
                finally
                {
                    IsSuccess = true;
                    window.Dispatcher.BeginInvoke(() =>
                    {
                        if (IsErr)
                        {
                            window.StatusProgressShowError("警告存在错误");
                        }
                        else window.StatusProgressShowComplete("完成");
                    });
                    Tools.CurrentCancelToken.Dispose();
                    Tools.CurrentCancelToken = null;
                    ScriptManage.ClearScripts();
                    GC.Collect();
                }
            }, Tools.CurrentCancelToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

        }

        public class ProcessStream
        {
            public readonly record struct ExecuteModule(AdvancedParametersSchemeModule module, SuccessAndProgress successAndProgress);
            public FileInfo ExecuteFileInfo;
            public ExecuteModule[] Modules;
            private string CachePath;
            public ProcessStream(FileInfo fileInfo, IEnumerable<AdvancedParametersSchemeModule> modules)
            {
                if (fileInfo is null)
                    throw new NullReferenceException("输入实例不能是Null");
                if (!fileInfo.IsExists)
                    throw new System.IO.FileNotFoundException(fileInfo.FullPath);
                if (modules is null)
                    throw new ArgumentNullException("方案流不能是null");
                if (!modules.Any())
                    throw new ArgumentNullException("方案流中没有处理方案");

                ExecuteFileInfo = fileInfo;
                Modules = modules.Select(p => new ExecuteModule(p.DeepCopy(), new SuccessAndProgress(p, fileInfo.FullPath))).ToArray();

                //配置输入输出
                int FLowIndex = 0;
                string Extension = null!;
                ParametersSign Output = null!;
                CachePath = Path.Combine(Config.CacheDirectory, $"{ExecuteFileInfo.Name}_Cache_{Tools.GenerateRandomString(5)}");
                if (!Directory.Exists(CachePath))
                    Directory.CreateDirectory(CachePath);
                foreach (ExecuteModule executeModule in Modules)
                {
                    if (!executeModule.module.GetInputs(out ParametersSign[] Inputs))
                        throw new ArgumentException($"源[{fileInfo.FullPath}]->流程[{executeModule.module.SchemeName}] 缺少输入参数");
                    int SelectIndex;
                    foreach (ParametersSign CurrentInputParame in Inputs)
                    {
                        if (!Tools.InputToIndex(CurrentInputParame.Parameter, out SelectIndex))
                            throw new ArgumentException($"源[{fileInfo.FullPath}]->流程[{executeModule.module.SchemeName}]->[{CurrentInputParame.SignName}]:输入参数异常，不是一个可以转换的值");

                        //当前流是否队列首个流 或者选择的输入是否是源的输入
                        if (FLowIndex < 1 || SelectIndex < 0)
                            CurrentInputParame.Parameter = fileInfo.FullPath;
                        else
                        {
                            if (SelectIndex >= FLowIndex)
                                throw new ArgumentException($"源[{fileInfo.FullPath}]->流程[{executeModule.module.SchemeName}]->[{CurrentInputParame.SignName}]" +
                                    "流输入选择错误,在流程中目标流晚于当前流，目标流的输出结果尚未产生，无法作为当前流的输入");
                            CurrentInputParame!.Parameter = Modules[SelectIndex].module.GetOutput().Parameter;
                        }
                    }

                    //输出
                    Output = executeModule.module.GetOutput();
                    Extension = Output.Parameter.Trim();
                    if (!Extension.StartsWith('.'))
                        Extension = '.' + Extension;

                    Output.Parameter = Tools.CheckDuplicateNameAndLegal(Path.Combine(CachePath, $"{fileInfo.Name}_{FLowIndex}{Extension}"));

                    ++FLowIndex;
                }
                //最后一个输出的指定路径
                if (Instance.IsEnableSourceDirectory)
                    Output.Parameter = Tools.CheckDuplicateNameAndLegal(Path.ChangeExtension(fileInfo.FullPath, Extension));
                else
                    Output.Parameter = Tools.CheckDuplicateNameAndLegal(Path.Combine(Instance.OutPath, $"{fileInfo.Name}{Extension}"));
            }

            public bool Execute()
            {
                bool IsSuccess = true;
                ExecuteModule LastModule = Modules.Last();
                foreach (ExecuteModule executeModule in Modules)
                {
                    if (Tools.IsCancellationRequested)
                        break;

                    string InFileFullPath = executeModule.module.GetInput().Parameter;
                    string OutFileFullPath = executeModule.module.GetOutput().Parameter;
                    string? OurDirectory = Path.GetDirectoryName(OutFileFullPath);

                    if (string.IsNullOrEmpty(InFileFullPath) || !File.Exists(InFileFullPath))
                        throw new ArgumentException($"文件[{ExecuteFileInfo.FullName}]过程[{executeModule.module.SchemeName}]错误:输入文件[{InFileFullPath}]不存在");
                    
                    if (string.IsNullOrEmpty(OutFileFullPath) || !Directory.Exists(OurDirectory))
                        throw new ArgumentException($"文件[{ExecuteFileInfo.FullName}]过程[{executeModule.module.SchemeName}]错误:输出路径[{OurDirectory}]不存在");

                    //最后一个流程，将写入最终目标路径，因此不允许覆盖，使用重命名方式更改最终输出的文件名，非最终流程的中间过程，中间结果不必保留，直接覆盖
                    if (executeModule == LastModule)
                    {
                        while (File.Exists(OutFileFullPath))
                        {
                            string ex = Path.GetExtension(OutFileFullPath)!;
                            executeModule.module.GetOutput().Parameter = OutFileFullPath = Path.Combine(OurDirectory, $"{Path.GetFileNameWithoutExtension(OutFileFullPath)}(重命名){ex}");
                        }
                    }
                    else
                    {
                        if (File.Exists(OutFileFullPath))
                            File.Delete(OutFileFullPath);
                    }

                    executeModule.successAndProgress.Init();
                    string args = executeModule.module.GenerateCommandArgument();
                    executeModule.successAndProgress.WriteLineNewInfo($"文件[{ExecuteFileInfo.FullPath}]过程[{executeModule.module.SchemeName}]");
                    executeModule.successAndProgress.WriteLineNewInfo($"{executeModule.module.ProcessorName} {args}");
                    CallShell.ShellTypeProcessNameIFSet(executeModule.module);

                    executeModule.successAndProgress.EnableWriteLog = executeModule.module.EnableSkipOutputSign;

                    if (InternalProcessorSelector.GetSelector(executeModule.module, out Action<AdvancedParametersSchemeModule, SuccessAndProgress> ProcessExecute))
                        ProcessExecute?.Invoke(executeModule.module, executeModule.successAndProgress);
                    else
                        ProcessShell.ProcessExecuteWaitForExit(executeModule.module.ProcessorName, args, executeModule.successAndProgress.SuccessValidated, executeModule.successAndProgress.SuccessValidated);

                    if (!executeModule.successAndProgress.IsSuccess)
                        IsSuccess = false;


                    if (executeModule.successAndProgress.EnableWriteLog)
                        File.WriteAllText(OutFileFullPath, executeModule.successAndProgress.Log);


                    LogManage.WriteLineNewInfo(executeModule.successAndProgress.Log);
                    executeModule.successAndProgress.ReleaseScript();

                    if (!Tools.IsCancellationRequested && !IsSuccess)
                        throw new FilesModesException($"执行失败 参数[{executeModule.module.ProcessorName}] [{args}] 错误:[{executeModule.successAndProgress.ErrorInfo}] 消息Log:[{executeModule.successAndProgress.Log}]");
                }
                ExecuteFileInfo.CompletionStatus = (IsSuccess && File.Exists(LastModule.module.GetOutput().Parameter)) ? CompletionStatusEnum.Success : CompletionStatusEnum.Fail;
                if (ExecuteFileInfo.CompletionStatus == CompletionStatusEnum.Success)
                {
                    if(Config.AutoClearCache)
                        Directory.Delete(CachePath, true);
                    return true;
                }
                else return false;
            }



            [Serializable]
            public class FilesModesException : Exception
            {
                public FilesModesException() { }
                public FilesModesException(string message) : base(message) { }
                public FilesModesException(string message, Exception inner) : base(message, inner) { }
                [Obsolete] protected FilesModesException(
                  System.Runtime.Serialization.SerializationInfo info,
                  System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
            }
        }
    }

}
